using System;
using System.ComponentModel;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;

using System.Reflection;

namespace InfFront.InfoSources.Base
{
    [DefaultProperty("MappingName")]
    public class MappedInfoField : InfoField
    {
        #region Private members
        private string mappingName;
        #endregion

        #region Initialization/Construction
        public MappedInfoField()
        {
        }

        public MappedInfoField(IContainer container)
            : base(container)
        {
            container.Add(this);
        }
        #endregion Initialization/Construction

        [Category("General")]
        public string MappingName
        {
            get { return mappingName; }
            set { mappingName = value; }
        }

        public override object GetValue(object obj)
        {
            return GetValue(obj, MappingName);
        }

        public override void SetValue(object obj, object value)
        {
            SetValue(obj, MappingName, value);
        }

        private static object GetValue(object obj, string propName)
        {
            if (obj == null)
                throw new ArgumentNullException("obj", "No object specified");

            int dotPos = propName.IndexOf('.');
            if (dotPos != -1)
            {
                obj = GetValue(obj, propName.Substring(0, dotPos));
                propName = propName.Substring(dotPos + 1);
            }

            PropertyDescriptor prop = TypeDescriptor.GetProperties(obj).Find(propName, false);
            if (prop != null)
                return prop.GetValue(obj);
            FieldInfo field = obj.GetType().GetField(propName);
            if (field != null)
                return field.GetValue(obj);

            // TODO: develop custom exception hierarchy

            throw new ArgumentException(string.Format("Object doesn't have a field or property with name '{0}'", propName), "MappingName");
        }

        private static void SetValue(object obj, string propName, object value)
        {
            if (obj == null)
                throw new ArgumentNullException("obj", "No object specified");

            int dotPos = propName.LastIndexOf('.');
            if (dotPos != -1)
            {
                obj = GetValue(obj, propName.Substring(0, dotPos));
                propName = propName.Substring(dotPos + 1);
            }

            PropertyDescriptor prop = TypeDescriptor.GetProperties(obj).Find(propName, false);
            if (prop != null)
                prop.SetValue(obj, value);
            else
            {
                FieldInfo field = obj.GetType().GetField(propName);
                if (field != null)
                    field.SetValue(obj, value);
            }
        }
    }
}
